// Copyright (c) Microsoft Corporation.  All rights reserved.

using System;
using System.Collections.Generic;
using System.Text;
using System.IO;
using Microsoft.SqlServer.Dts.Pipeline;
using Microsoft.SqlServer.Dts.Pipeline.Wrapper;
using Microsoft.SqlServer.Dts.Runtime.Wrapper;

namespace Microsoft.Samples.SqlServer.SSIS.EventLogSource
{
    [DtsPipelineComponent(DisplayName = "Eventlog Source",
        //IconResource = "app.ico",
        Description = "Imports Windows Event logs",
        ComponentType = ComponentType.SourceAdapter)]
    public class EventLogSource : PipelineComponent
    {
        private static string inputFilePropertyLabel = "Input EventLog File";
        private static string inputFileTypeLabel = "EventLog File Type";

        public EventLogSource() { }

        private string GetInputFileName()
        {
            return this.ComponentMetaData.CustomPropertyCollection[EventLogSource.inputFilePropertyLabel].Value.ToString();
        }
        private string GetLogType()
        {
            return this.ComponentMetaData.CustomPropertyCollection[EventLogSource.inputFileTypeLabel].Value.ToString();
        }

        public override void ProvideComponentProperties()
        {
            base.ProvideComponentProperties();

            IDTSCustomProperty90 eventLogFileType = this.ComponentMetaData.CustomPropertyCollection.New();
            eventLogFileType.Name = EventLogSource.inputFileTypeLabel;
            eventLogFileType.Description = "Type of EventLog file to be imported (Application, System, Security, etc)";
            eventLogFileType.Value = "Application";

            IDTSCustomProperty90 eventLogFilePath = this.ComponentMetaData.CustomPropertyCollection.New();
            eventLogFilePath.Name = EventLogSource.inputFilePropertyLabel;
            eventLogFilePath.Description = "Full path to the EventLog file to be imported";
            eventLogFilePath.Value = "";

            // Allow the property to be an expression that evaluates to a filename
            eventLogFilePath.ExpressionType = DTSCustomPropertyExpressionType.CPET_NOTIFY;
            // Add the ability to bring up the file browse dialog when the elipsis is clicked
            eventLogFilePath.UITypeEditor = "System.Windows.Forms.Design.FileNameEditor, System.Design, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a";

            // There are no inputs to this component, so remove them
            this.ComponentMetaData.InputCollection.RemoveAll();

            // Remove the default outputs and add what is needed
            this.ComponentMetaData.OutputCollection.RemoveAll();

            // Add an output for valid rows
            IDTSOutput90 output = this.ComponentMetaData.OutputCollection.New();
            output.Name = "EventLog Records";

            IDTSOutputColumn90 col = output.OutputColumnCollection.New();
            col.SetDataTypeProperties(DataType.DT_WSTR, 4000, 0, 0, 0);
            col.Name = "EventFileName";

            col = output.OutputColumnCollection.New();
            col.SetDataTypeProperties(DataType.DT_WSTR, 64, 0, 0, 0);
            col.Name = "EventFileType";

            col = output.OutputColumnCollection.New();
            col.SetDataTypeProperties(DataType.DT_I4, 0, 0, 0, 0);
            col.Name = "RecordNumber";

            col = output.OutputColumnCollection.New();
            col.SetDataTypeProperties(DataType.DT_WSTR, 16, 0, 0, 0);
            col.Name = "Type";

            col = output.OutputColumnCollection.New();
            col.SetDataTypeProperties(DataType.DT_DBTIMESTAMP, 0, 0, 0, 0);
            col.Name = "TimeGeneratedUTC";

            col = output.OutputColumnCollection.New();
            col.SetDataTypeProperties(DataType.DT_DBTIMESTAMP, 0, 0, 0, 0);
            col.Name = "TimeWrittenUTC";

            col = output.OutputColumnCollection.New();
            col.SetDataTypeProperties(DataType.DT_WSTR, 4000, 0, 0, 0);
            col.Name = "Source";

            col = output.OutputColumnCollection.New();
            col.SetDataTypeProperties(DataType.DT_I2, 0, 0, 0, 0);
            col.Name = "Category";

            col = output.OutputColumnCollection.New();
            col.SetDataTypeProperties(DataType.DT_I2, 0, 0, 0, 0);
            col.Name = "MessageId";

            col = output.OutputColumnCollection.New();
            col.SetDataTypeProperties(DataType.DT_WSTR, 4000, 0, 0, 0);
            col.Name = "Sid";

            col = output.OutputColumnCollection.New();
            col.SetDataTypeProperties(DataType.DT_WSTR, 64, 0, 0, 0);
            col.Name = "ComputerName";

            col = output.OutputColumnCollection.New();
            col.SetDataTypeProperties(DataType.DT_WSTR, 4000, 0, 0, 0);
            col.Name = "MessageStrings";

            col = output.OutputColumnCollection.New();
            col.SetDataTypeProperties(DataType.DT_BYTES, 8000, 0, 0, 0);
            col.Name = "Data";
        }

        public override void PrimeOutput(int outputs, int[] outputIDs, PipelineBuffer[] buffers)
        {
            // pick up the custom property values
            string inputFile = GetInputFileName();
            string logType = GetLogType();
            EventFile eventFile = null;

            if (inputFile[0] == '@')
            {
                inputFile = GetVariableValue(inputFile);
            }

            // open the backup eventlog, read it and load the output
            try
            {
                eventFile = new EventFile(inputFile, logType);
                StringBuilder messageStrings = new StringBuilder();

                foreach (EventRecord record in eventFile)
                {

                    // Fill the buffer columns
                    buffers[0].AddRow();
                    buffers[0].SetString(0, eventFile.FileName);
                    buffers[0].SetString(1, eventFile.SourceName);
                    buffers[0].SetInt32(2, (Int32)record.RecordNumber);
                    buffers[0].SetString(3, Enum.GetName(typeof(EventType), record.Type));
                    buffers[0].SetDateTime(4, record.TimeGeneratedUTC);
                    buffers[0].SetDateTime(5, record.TimeWrittenUTC);
                    buffers[0].SetString(6, record.Source);
                    buffers[0].SetInt16(7, (Int16)record.Category);
                    buffers[0].SetInt16(8, (Int16)record.MessageId);
                    buffers[0].SetString(9, record.Sid == null ? string.Empty : record.Sid.ToString());
                    buffers[0].SetString(10, record.ComputerName);

                    // Since MessageStrings is a multi-valued field (an array), tab seperate it so it can be placed in a relational table
                    if (record.NumStrings > 0)
                    {
                        messageStrings.Length = 0;
                        for (int i = 0; i < record.NumStrings; i++)
                        {
                            messageStrings.AppendFormat("{0}\t", record.MessageStrings[i]);
                        }
                        messageStrings.Length -= 1;  //remove the last tab
                        buffers[0].SetString(11, messageStrings.ToString());
                    }
                    else
                    {
                        buffers[0].SetString(11, string.Empty);
                    }

                    buffers[0].SetBytes(12, record.Data == null ? new Byte[] { 0 } : record.Data);
                }
            }
            catch (System.ComponentModel.Win32Exception ex)
            {
                bool cancel;
                this.ComponentMetaData.FireError(ex.ErrorCode, ex.Source, ex.Message, ex.HelpLink, 0, out cancel);
            }
            finally
            {
                buffers[0].SetEndOfRowset();
                if(eventFile != null) eventFile.Dispose();
            }
        }

        private string GetVariableName(string qualifiedVariableName)
        {
            int openBracket = qualifiedVariableName.IndexOf('[');
            int closeBracket = qualifiedVariableName.IndexOf(']');
            return qualifiedVariableName.Substring(openBracket + 1, closeBracket - openBracket - 1);
        }

        private bool VariableExists(string variableName)
        {
            bool retVal = false;
            IDTSVariables90 variables = null;

            this.VariableDispenser.LockOneForRead(variableName, ref variables);
            retVal = variables != null && variables.Count == 1;

            if (variables.Locked)
            {
                variables.Unlock();
            }

            return retVal;
        }

        private string GetVariableValue(string qualifiedVariableName)
        {
            string retVal = null;

            string variableName = GetVariableName(qualifiedVariableName);
            IDTSVariables90 variables = null;

            this.VariableDispenser.LockOneForRead(variableName, ref variables);

            retVal = variables[0].Value as string;

            if (variables.Locked)
            {
                variables.Unlock();
            }

            return retVal;
        }

        // "@[User.EventLogFileName]"
        public override DTSValidationStatus Validate()
        {
            string filePropertyValue = GetInputFileName();

            if (filePropertyValue == null || filePropertyValue == string.Empty)
            {
                return DTSValidationStatus.VS_ISBROKEN;
            }
            else if (filePropertyValue[0] == '@')
            {
                string variableName = GetVariableName(filePropertyValue);

                if (!VariableExists(variableName))
                {
                    bool cancel;

                    this.ComponentMetaData.FireError(
                        Microsoft.SqlServer.Dts.Runtime.HResults.DTS_E_VALIDATIONFAILED,
                        EventLogSource.inputFilePropertyLabel, "The variable file does not exist", null, 0, out cancel);
                    return DTSValidationStatus.VS_ISBROKEN;
                }

            }
            else if (!File.Exists(filePropertyValue))
            {
                bool cancel;

                this.ComponentMetaData.FireError(
                    Microsoft.SqlServer.Dts.Runtime.HResults.DTS_E_VALIDATIONFAILED,
                    EventLogSource.inputFilePropertyLabel, "The specified file does not exist", null, 0, out cancel);
                return DTSValidationStatus.VS_ISBROKEN;
            }

            string logType = GetLogType();
            if (logType == null || logType == string.Empty)
            {
                return DTSValidationStatus.VS_ISBROKEN;
            }

            return base.Validate();
        }
    }
}
